🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / takserver / src / commonTest / kotlin / org / meshtastic / core / takserver / TakV2CompressorTaktalkTest.kt
Displaying Raw • Download
core/takserver/src/commonTest/kotlin/org/meshtastic/core/takserver/TakV2CompressorTaktalkTest.kt 65a1f5ce4d0e4b8eafcd0bbfccafd41dc6ee888a (65a1f5ce) Text, 9.16 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.takserver
Tff7b72import T7ee787org.meshtastic.proto.CotType
Tff7b72import T7ee787org.meshtastic.proto.GeoChat
Tff7b72import T7ee787org.meshtastic.proto.TAKPacketV2
Tff7b72import T7ee787org.meshtastic.proto.TakTalkMessage
Tff7b72import T7ee787org.meshtastic.proto.TakTalkRoomData
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertEquals
Tff7b72import T7ee787kotlin.test.assertNotNull
Tff7b72import T7ee787kotlin.test.assertNull
Tff7b72import T7ee787kotlin.test.assertTrue
T8b949e/**
* Regression tests for the TakV2Compressor adapter layer's TAKTALK handling.
*
* Before 0.3.0, the wire-to-SDK and SDK-to-wire converters in TakV2Compressor.kt had no cases for [TakTalkMessage] or
* [TakTalkRoomData]. m-t-t and y- events therefore fell through to [TakPacketV2Data.Payload.None] on the receive side,
* which surfaced as TAKTALK voice push-to-talk messages never reaching the TTS stage on the receiving ATAK device. Chat
* (b-t-f) was wired but its TAKTALK sidecars (lang, room_id, voice_profile_id) were dropped too.
*
* These tests pin the round-trip so any future regression of the same adapter gaps fails fast at unit-test time instead
* of in the field.
*/
Tff7b72class T56d364TakV2CompressorTaktalkTest Tb4b4b4{
Tf0883e@Test
Tff7b72fun Td2a8ff`m-t-t taktalk message round-trips with voice marker`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_m_t_tTb4b4b4,
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffTAKTALK-MESSAGE-81b44b48-21c5-4edf-aab2-b6bd2ec1f52fTa5d6ff"Tb4b4b4,
Te6edf3taktalk Tff7b72=
Te6edf3TakTalkMessageTb4b4b4(
Te6edf3text Tff7b72= Ta5d6ff"Ta5d6ffTesting 123, testing 123.Ta5d6ff"Tb4b4b4,
Te6edf3chatroom_id Tff7b72= Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4,
Te6edf3lang Tff7b72= Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4,
Te6edf3from_voice Tff7b72= Tff7b72trueTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3wire Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3wireTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3CotTypeTb4b4b4.Te6edf3CotType_m_t_tTb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3cot_type_idTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3callsignTb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3taktalkTb4b4b4, Ta5d6ff"Ta5d6fftaktalk variant must survive round-tripTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffTesting 123, testing 123.Ta5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3textTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3chatroom_idTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3langTb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3from_voiceTb4b4b4, Ta5d6ff"Ta5d6ff<voice/> marker must surviveTa5d6ff"Tb4b4b4)
T8b949e// v0.4.0+: `bool pli` was removed (PLI is implicit). A populated `taktalk`
T8b949e// arm already proves this did not degrade to an (implicit) PLI.
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`m-t-t taktalk text-only message has from_voice false`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_m_t_tTb4b4b4,
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffETHELTa5d6ff"Tb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffTAKTALK-MESSAGE-text-onlyTa5d6ff"Tb4b4b4,
Te6edf3taktalk Tff7b72=
Te6edf3TakTalkMessageTb4b4b4(Te6edf3text Tff7b72= Ta5d6ff"Ta5d6fftyped messageTa5d6ff"Tb4b4b4, Te6edf3chatroom_id Tff7b72= Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4, Te6edf3lang Tff7b72= Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4, Te6edf3from_voice Tff7b72= Tff7b72falseTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)Tb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3taktalkTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6fftyped messageTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3textTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Tff7b72falseTb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalkTff7b72!!Tb4b4b4.Te6edf3from_voiceTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`y- taktalk room broadcast round-trips with participant list`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_yTb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffROOM-DATA-5eac9336-2698-4dbb-9fa2-62b100e469feTa5d6ff"Tb4b4b4,
T8b949e// v0.3.2: sender identity lives on envelope packet.callsign,
T8b949e// not on the deprecated TakTalkRoomData.sender_callsign field.
T8b949e// The SDK builder reconstitutes <sender-callsign> from envelope
T8b949e// callsign on emit.
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4,
Te6edf3taktalk_room Tff7b72=
Te6edf3TakTalkRoomDataTb4b4b4(
Te6edf3room_id Tff7b72= Ta5d6ff"Ta5d6ff30b2755c-c547-44ef-a0cc-cdbd8a15616fTa5d6ff"Tb4b4b4,
Te6edf3room_name Tff7b72= Ta5d6ff"Ta5d6fftestTa5d6ff"Tb4b4b4,
Te6edf3participants Tff7b72= Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffETHELTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3CotTypeTb4b4b4.Te6edf3CotType_yTb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3cot_type_idTb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3taktalk_roomTb4b4b4, Ta5d6ff"Ta5d6fftaktalk_room variant must survive round-tripTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4,
Te6edf3decompressedTb4b4b4.Te6edf3callsignTb4b4b4,
Ta5d6ff"Ta5d6ffv0.3.2: <sender-callsign> routes to envelope callsign on round-tripTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff30b2755c-c547-44ef-a0cc-cdbd8a15616fTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalk_roomTff7b72!!Tb4b4b4.Te6edf3room_idTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6fftestTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalk_roomTff7b72!!Tb4b4b4.Te6edf3room_nameTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffETHELTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3taktalk_roomTff7b72!!Tb4b4b4.Te6edf3participantsTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`m-t-t with marti round-trips dest callsigns and survives compression`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_m_t_tTb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffTAKTALK-MESSAGE-marti-test-uuidTa5d6ff"Tb4b4b4,
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4,
Te6edf3taktalk Tff7b72=
Te6edf3TakTalkMessageTb4b4b4(
Te6edf3text Tff7b72= Ta5d6ff"Ta5d6ffPush-to-talk to ETHELTa5d6ff"Tb4b4b4,
Te6edf3chatroom_id Tff7b72= Ta5d6ff"Ta5d6ff1Ta5d6ff"Tb4b4b4,
Te6edf3lang Tff7b72= Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4,
Te6edf3from_voice Tff7b72= Tff7b72trueTb4b4b4,
Tb4b4b4)Tb4b4b4,
T8b949e// Directed-routing recipient list. TAKTALK gates voice TTS on
T8b949e// this list matching the receiver's callsign — a regression
T8b949e// here silently breaks voice messaging end-to-end.
Te6edf3marti Tff7b72= Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3protoTb4b4b4.Te6edf3MartiTb4b4b4(Te6edf3dest_callsign Tff7b72= Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffETHELTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Te6edf3CotTypeTb4b4b4.Te6edf3CotType_m_t_tTb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3cot_type_idTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3callsignTb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3martiTb4b4b4, Ta5d6ff"Ta5d6ffmarti must survive compression round-tripTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Te6edf3listOfTb4b4b4(Ta5d6ff"Ta5d6ffETHELTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3decompressedTb4b4b4.Te6edf3martiTff7b72!!Tb4b4b4.Te6edf3dest_callsignTb4b4b4,
Ta5d6ff"Ta5d6ffmarti.dest_callsign must round-trip unchanged so TAKTALK can resolve TTS targetsTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`b-t-f chat carries TAKTALK sidecars through compressor`Tb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_b_t_fTb4b4b4,
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffASPENTa5d6ff"Tb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffGeoChat.testTa5d6ff"Tb4b4b4,
Te6edf3chat Tff7b72=
Te6edf3GeoChatTb4b4b4(
Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffTest messageTa5d6ff"Tb4b4b4,
Te6edf3lang Tff7b72= Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4,
Te6edf3room_id Tff7b72= Ta5d6ff"Ta5d6ff30b2755c-c547-44ef-a0cc-cdbd8a15616fTa5d6ff"Tb4b4b4,
Te6edf3voice_profile_id Tff7b72= Ta5d6ff"Ta5d6ff"Tb4b4b4, T8b949e// empty marker `<voice_profile_id/>`
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)Tb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3chatTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffTest messageTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3messageTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffEnglishTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3langTb4b4b4, Ta5d6ff"Ta5d6ffTAKTALK <Ea> lang must surviveTa5d6ff"Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(
Ta5d6ff"Ta5d6ff30b2755c-c547-44ef-a0cc-cdbd8a15616fTa5d6ff"Tb4b4b4,
Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3room_idTb4b4b4,
Ta5d6ff"Ta5d6ffTAKTALK <roomId> must surviveTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3assertNotNullTb4b4b4(
Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3voice_profile_idTb4b4b4,
Ta5d6ff"Ta5d6ffempty <voice_profile_id/> marker must survive as empty stringTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3voice_profile_idTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8ff`b-t-f chat without TAKTALK sidecars leaves them null on the wire`Tb4b4b4(Tb4b4b4) Tb4b4b4{
T8b949e// Plain GeoChat from non-TAKTALK ATAK — no sidecars present. The
T8b949e// round-trip must not synthesize empty-string sidecars that would
T8b949e// change the rebuilt CoT XML for plain chat messages.
Tff7b72val Te6edf3original Tff7b72=
Te6edf3TAKPacketV2Tb4b4b4(
Te6edf3cot_type_id Tff7b72= Te6edf3CotTypeTb4b4b4.Te6edf3CotType_b_t_fTb4b4b4,
Te6edf3callsign Tff7b72= Ta5d6ff"Ta5d6ffALPHA-1Ta5d6ff"Tb4b4b4,
Te6edf3uid Tff7b72= Ta5d6ff"Ta5d6ffGeoChat.plainTa5d6ff"Tb4b4b4,
Te6edf3chat Tff7b72=
Te6edf3GeoChatTb4b4b4(
Te6edf3message Tff7b72= Ta5d6ff"Ta5d6ffplain chatTa5d6ff"Tb4b4b4,
T8b949e// no lang, no room_id, no voice_profile_id
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3decompressed Tff7b72= Te6edf3TakV2CompressorTb4b4b4.Te6edf3decompressTb4b4b4(Te6edf3TakV2CompressorTb4b4b4.Te6edf3compressTb4b4b4(Te6edf3originalTb4b4b4)Tb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3chatTb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ffplain chatTa5d6ff"Tb4b4b4, Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3messageTb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3langTb4b4b4, Ta5d6ff"Ta5d6ffnon-TAKTALK chat should have null lang on the wireTa5d6ff"Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3room_idTb4b4b4, Ta5d6ff"Ta5d6ffnon-TAKTALK chat should have null room_idTa5d6ff"Tb4b4b4)
Te6edf3assertNullTb4b4b4(Te6edf3decompressedTb4b4b4.Te6edf3chatTff7b72!!Tb4b4b4.Te6edf3voice_profile_idTb4b4b4, Ta5d6ff"Ta5d6ffnon-TAKTALK chat should have null voice_profile_idTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.06s